/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.blesdk;

import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;

/**
 * Service for managing connection and data communication with a GATT server
 * hosted on a given BlueTooth LE device.
 */
@SuppressLint("NewApi")
public class BluetoothLeService extends Service {

	private BluetoothManager mBluetoothManager;
	private BluetoothAdapter mBluetoothAdapter;
	private BluetoothGatt mBluetoothGatt;
	public int mConnectionState = Constant.STATE_DISCONNECTED;
	private Handler mHandler=new Handler();
	int level = 1;
	int usersex =0;
	int age = 20;
	float high = 1.70f;// 单位
	private final static int TIME_OUT = 90*1000;
	private BluetoothGattService bleGattService;
	private BluetoothGattCharacteristic characteristicWrite;
	private StopLeScanRunnable runnable;

	@Override
	public void onCreate() {
		super.onCreate();
		runnable = new StopLeScanRunnable();
		Logutil.setDebug(false);
	}

	private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

		@Override
		public void onConnectionStateChange(BluetoothGatt gatt, int status,
				int newState) {
			Logutil.e("=======status:" + status+",newState==="+newState);
			if (status == BluetoothGatt.GATT_SUCCESS) {
				if (newState == BluetoothProfile.STATE_CONNECTED) {
					Logutil.e("------connectedSuccess-----");
					boolean discover = mBluetoothGatt.discoverServices();
					Logutil.e("-------discover service--" + discover + "-------");
					sendBroadCast(Constant.ACTION_GATT_CONNECTED,
							Constant.ACTION_GATT_CONNECTED, getString(R.string.connectedSuccessful));
					mConnectionState = Constant.STATE_CONNECTED;
				} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
					Logutil.e( "-----disconnected-----");
//					boolean b = refreshDeviceCache();
//					Logutil.e("----refresh--" + b);
					sendBroadCast(Constant.ACTION_GATT_DISCONNECTED,
							Constant.ACTION_GATT_DISCONNECTED, getString(R.string.connectedFail));
					mConnectionState = Constant.STATE_DISCONNECTED;
					gatt.disconnect();
					gatt.close();
					//TODO do reconnect
				}else if(newState == BluetoothProfile.STATE_CONNECTING){
					Logutil.e( "-------connecting-----");
					sendBroadCast(Constant.ACTION_GATT_CONNECTING,
							Constant.ACTION_GATT_CONNECTING, getString(R.string.connecting));
					mConnectionState = Constant.STATE_CONNECTING;
				}else if(newState == BluetoothProfile.STATE_DISCONNECTING){
					Logutil.e( "-------断开中-----");
					sendBroadCast(Constant.ACTION_GATT_DISCONNECTING,
							Constant.ACTION_GATT_DISCONNECTING, getString(R.string.unConnecting));
					mConnectionState = Constant.STATE_DISCONNECTING;
				}
			}else{
				//Hardware initiative/abnormal disconnect
				Logutil.e( "-----connectedFail-----");
				sendBroadCast(Constant.ACTION_GATT_DISCONNECTED,
						Constant.ACTION_GATT_DISCONNECTED, getString(R.string.connectedFail));
				mConnectionState = Constant.STATE_DISCONNECTED;
				gatt.disconnect();
				gatt.close();
				//TODO do reconnect
			}
		}

		@Override
		public void onServicesDiscovered(BluetoothGatt gatt, int status) {
			Logutil.e( "------onServicesDiscovered----");
			// model=1 1910 model=2 fff0
			int model = 0;
			List<BluetoothGattService> services = gatt.getServices();
			for (int i = 0; i < services.size(); i++) {
				String serviceUUID = services.get(i).getUuid().toString();
				Logutil.i("----serviceUUID----" + serviceUUID);
				if(serviceUUID.equals(Constant.SERVICE_UUID_01)){
					model = 1;
					Logutil.e("------model 1------");
					break;
				}else if(serviceUUID.equals(Constant.SERVICE_UUID_02)){
					model = 2;
					Logutil.e("------model 2------");
					break;
				}
			}

			if((model==1)||getDeviceName().equals(Constant.DEVICE_NAME_EAR)){
				Logutil.e( "-----ear-/-model 01---");
				bleGattService = gatt
						.getService(UUID.fromString(Constant.SERVICE_UUID_01));
				//only read
				BluetoothGattCharacteristic characteristicRead = bleGattService
						.getCharacteristic(UUID.fromString(Constant.CH_01));
				setCharacterValue(characteristicRead,null, status);
			}else if((model==2)){
				if(getDeviceName().equals(Constant.DEVICE_NAME_FAT3)){
					Logutil.e( "-----Holtek_BodyFatScal------");
					bleGattService = gatt
							.getService(UUID.fromString(Constant.SERVICE_UUID_02));
					//write
					characteristicWrite = bleGattService
							.getCharacteristic(UUID.fromString(Constant.UUID_WRITEFAT));
					//read
					BluetoothGattCharacteristic characteristicRead = bleGattService
							.getCharacteristic(UUID.fromString(Constant.UUID_REDFAT));
					//notify
					BluetoothGattCharacteristic	characteristicNotify = bleGattService
							.getCharacteristic(UUID.fromString(Constant.UUID_CH_SERVICE_NOTIFY));
					setCharacterValue(characteristicRead, characteristicNotify, status);
				}else if(getDeviceName().equals(Constant.DEVICE_NAME_FAT1)||
						getDeviceName().equals(Constant.DEVICE_NAME_FAT2)||
						getDeviceName().contains(Constant.DEVICE_NAME_FAT)){
					Logutil.e( "-----fatScale------");
					bleGattService = gatt
							.getService(UUID.fromString(Constant.SERVICE_UUID_02));
					//read
					BluetoothGattCharacteristic	characteristicRead = bleGattService
							.getCharacteristic(UUID.fromString(Constant.UUID_REDFAT));
					//notify
					BluetoothGattCharacteristic	characteristicNotify = bleGattService
							.getCharacteristic(UUID.fromString(Constant.UUID_CH_SERVICE_NOTIFY));
					setCharacterValue(characteristicRead, characteristicNotify , status);
				}else{
					Logutil.e("-------genenral model------");
					bleGattService = gatt
							.getService(UUID.fromString(Constant.SERVICE_UUID_02));
					//only read
					BluetoothGattCharacteristic characteristicRead = bleGattService
							.getCharacteristic(UUID.fromString(Constant.TURGOSCOPE_WRITE_CHARA));
					setCharacterValue(characteristicRead,null, status);
				}
			}
		}

		public void onCharacteristicWrite(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic, int status) {
			sendBroadCast(Constant.WRITE_SUCCEED, Constant.WRITE_SUCCEED,
					getString(R.string.writedSuccessful));
		}

		@Override
		public void onCharacteristicChanged(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic) {
			Logutil.e( "--------onCharacteristicChanged---------");
			if (characteristic.getValue() != null) {
				/*Logutil.e("----value----" + characteristic.getValue().length);
				for (int i = 0; i < characteristic.getValue().length; i++) {
					Logutil.e("----value["+i+"] = " + characteristic.getValue()[i]);
				}*/
				String codeString= Utils.bytesToHexString(characteristic.getValue());
				if(codeString!=null && !codeString.equals("null")){
					Logutil.e( "codeString==="+codeString);
					//Instead of doing processing, the user interprets it himself
					sendBroadCast(Constant.ACTION_DATA_EXTRA_DATA, Constant.ACTION_DATA_EXTRA_DATA,codeString);
				}
			}
		}
	};

	private void setCharacterValue(BluetoothGattCharacteristic characteristic,
			BluetoothGattCharacteristic characteristic1,int status){
		// 激活通知
		final int charaProp = characteristic.getProperties();
		int charaProp_second = -1;
		if (characteristic1!=null) {
			charaProp_second = characteristic1.getProperties();
		}
		if (status == BluetoothGatt.GATT_SUCCESS) {
			if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) >0) {
				Logutil.e("--------具有通知属性----charaProp-" + charaProp);
				mBluetoothGatt.setCharacteristicNotification(
						characteristic, true);
				BluetoothGattDescriptor descriptor = characteristic
						.getDescriptor(UUID
								.fromString(Constant.CHARACTERISTIC_UUID_RETURN));
				if (descriptor != null) {
					descriptor
					.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
					Logutil.e( characteristic.getUuid() +
							"激活通知属性： " + mBluetoothGatt.writeDescriptor(descriptor));
					if (characteristic1==null) {
						sendBroadCast(Constant.ACTION_GATT_CONNECTED,
								Constant.ACTION_GATT_CONNECTED, getString(R.string.connectedSuccessful));// 激活通知属性发送广播
					}
				}
				if(characteristic1==null)
					return;
				if ((charaProp_second & BluetoothGattCharacteristic.PROPERTY_NOTIFY) >0) {
					Logutil.e("--------具有通知属性-------");
					mBluetoothGatt.setCharacteristicNotification(
							characteristic1, true);
					if (descriptor != null) {
						descriptor
						.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
						Logutil.e(characteristic1.getUuid() + "激活通知属性： "
								+ mBluetoothGatt.writeDescriptor(descriptor));
						Logutil.e("----------体重称连接成功---------------");
						sendBroadCast(Constant.ACTION_GATT_CONNECTED,
								Constant.ACTION_GATT_CONNECTED, getString(R.string.connectedSuccessful));// 激活通知属性发送广播
					}
				}
			}
		}
	}

	private String deviceName;
	public void setName(String name){
		this.deviceName = name;
		Logutil.e("----deviceName----" + deviceName);
		scanLeDevice(true);
	}

	private String getDeviceName(){
		return this.deviceName;
	}

	/**
	 * 向需要数据处理地方发送广播
	 * @param action
	 * @param key
	 * @param value
	 */
	private void sendBroadCast(String action,String key,String value){
		Intent intent = new Intent(action);
		intent.putExtra(key, value);
		sendBroadcast(intent);
	}

	public class LocalBinder extends Binder {
		public BluetoothLeService getService() {
			return BluetoothLeService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	@Override
	public boolean onUnbind(Intent intent) {
		close();
		return super.onUnbind(intent);
	}

	@SuppressWarnings("deprecation")
	@TargetApi(18)
	@SuppressLint("NewApi")
	public void scanLeDevice(final boolean enable) {
		Logutil.e( "---scanLeDevice-name--" + deviceName);
		if (enable) {
			// Stops scanning after a pre-defined scan period.
			mHandler.postDelayed(runnable, TIME_OUT);
			mBluetoothAdapter.startLeScan(mLeScanCallback);
		} else {
			mBluetoothAdapter.stopLeScan(mLeScanCallback);
		}
	}

	private class StopLeScanRunnable implements Runnable{
		@SuppressWarnings("deprecation")
		@Override
		public void run() {
			mBluetoothAdapter.stopLeScan(mLeScanCallback);
			Logutil.e("------timeout--stopScan---------");
			if(mConnectionState == Constant.STATE_SEARCHING){
				sendBroadCast(Constant.ACTION_SEARCHFAIL, Constant.ACTION_SEARCHFAIL,
						getString(R.string.searchFailDevice));
				mConnectionState = Constant.STATE_SEARCHFAIL;
				Logutil.e("--------searchedFail---------");
			}
		}
	}

	@SuppressLint("NewApi")
	private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

		@Override
		public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
			Logutil.e( "----onLeScan----" + device.getName());
			sendBroadCast(Constant.ACTION_SEARCHING, Constant.ACTION_SEARCHING,
					getString(R.string.searchingDevice));
			mConnectionState = Constant.STATE_SEARCHING;
			if(getDeviceName().equals(device.getName())){
				Logutil.e( "----Scanned----" + device.getName());
				sendBroadCast(Constant.SEARCH_DEVICE,
						Constant.SEARCH_DEVICE,
						"搜到设备"+device.getName()+","+device.getAddress());
				scanLeDevice(false);
				mHandler.removeCallbacks(runnable);
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//针对三星连接不上的问题，由于连接太快导致，延迟100ms
				mHandler.postDelayed(new Runnable() {
					@Override
					public void run() {
						connect(device.getAddress());
					}
				}, 100);
			}
		}
	};

	private final IBinder mBinder = new LocalBinder();

	/**
	 * Initializes a reference to the local Bluetooth adapter.
	 *
	 * @return Return true if the initialization is successful.
	 */
	public boolean initialize() {
		// For API level 18 and above, get a reference to BluetoothAdapter
		// through
		// BluetoothManager.
		if (mBluetoothManager == null) {
			mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
			if (mBluetoothManager == null) {
				Logutil.e( "Unable to initialize BluetoothManager.");
				return false;
			}
		}

		mBluetoothAdapter = mBluetoothManager.getAdapter();
		if (mBluetoothAdapter == null) {
			Logutil.e( "Unable to obtain a BluetoothAdapter.");
			return false;
		}
		return true;
	}

	/**
	 * Need to call after the initialize method
	 * {@link initialize}
	 */
	public void setEnable(){
		checkInitialState();
		if(!mBluetoothAdapter.isEnabled())
			mBluetoothAdapter.enable();
	}

	/**
	 * confirm the Ble is open
	 * @return
	 */
	public boolean isEnable(){
		return mBluetoothAdapter.enable();
	}

	/**
	 * check the bluetoothAdapter is initial
	 */
	private void checkInitialState() {
		if (mBluetoothManager == null) {
			mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
			if (mBluetoothManager == null) {
				Logutil.e( "Unable to initialize BluetoothManager.");
				return ;
			}
		}

		if (mBluetoothAdapter == null) {
			mBluetoothAdapter = mBluetoothManager.getAdapter();
			if (mBluetoothAdapter == null) {
				Logutil.e( "Unable to obtain a BluetoothAdapter.");
				return ;
			}
		}
	}

	/**
	 * Connects to the GATT server hosted on the BlueTooth LE device.
	 *
	 * @param address
	 *            The device address of the destination device.
	 *
	 * @return Return true if the connection is initiated successfully. The
	 *         connection result is reported asynchronously through the
	 *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
	 *         callback.
	 */
	public boolean connect(final String address) {
		if (mBluetoothAdapter == null || address == null) {
			Logutil.e(
					"BluetoothAdapter not initialized or unspecified address.");
			return false;
		}
		// Previously connected device. Try to reconnect.
		/*if (mBluetoothDeviceAddress != null
				&& address.equals(mBluetoothDeviceAddress)
				&& mBluetoothGatt != null) {
			Log.d(Tag,"Trying to use an existing mBluetoothGatt for connection.");
			if (mBluetoothGatt.connect()) {
				//mConnectionState = STATE_CONNECTING;
				return true;
			} else {
				return false;
			}
		}*/

		final BluetoothDevice device = mBluetoothAdapter
				.getRemoteDevice(address);
		if (device == null) {
			Logutil.w("Device not found.  Unable to connect.");
			return false;
		}
		// We want to directly connect to the device, so we are setting the
		// autoConnect
		// parameter to false.
		Logutil.e( "-----开始连接蓝牙设备-----");
		sendBroadCast(Constant.ACTION_GATT_CONNECTING,
				Constant.ACTION_GATT_CONNECTING, getString(R.string.connecting));
		mConnectionState = Constant.STATE_CONNECTING;
		mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
		Logutil.d("Trying to create a new connection.");
		return true;
	}

	/**
	 * Disconnects an existing connection or cancel a pending connection. The
	 * disconnection result is reported asynchronously through the
	 * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
	 * callback.
	 */
	public void disconnect() {
		if (mBluetoothAdapter == null || mBluetoothGatt == null) {
			Logutil.w("BluetoothAdapter not initialized");
			return;
		}
		Logutil.e( "-------断开中-----");
		sendBroadCast(Constant.ACTION_GATT_DISCONNECTING,
				Constant.ACTION_GATT_DISCONNECTING, getString(R.string.unConnecting));
		mConnectionState = Constant.STATE_DISCONNECTING;
		mBluetoothGatt.disconnect();
	}

	/**
	 * After using a given BLE device, the app must call this method to ensure
	 * resources are released properly.
	 */
	public void close() {
		if (mBluetoothGatt == null) {
			return;
		}
		mBluetoothGatt.close();
		mBluetoothGatt = null;
	}
	
	/**
	 * Refresh device cache
	 * @return
	 */
	private boolean refreshDeviceCache() {
		if (mBluetoothGatt != null) {
			try {
				BluetoothGatt localBluetoothGatt = mBluetoothGatt;
				Method localMethod = localBluetoothGatt.getClass().getMethod("refresh", new Class[0]);
				if (localMethod != null) {
					boolean bool = ((Boolean) localMethod.invoke(localBluetoothGatt, new Object[0])).booleanValue();
					return bool;
				}
			} catch (Exception localException) {
				Logutil.e("----An exception occurred while refreshing device----");
			}
		}
		return false;
	}

	public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {

		if (mBluetoothAdapter == null || mBluetoothGatt == null) {
			Logutil.w("BluetoothAdapter not initialized");
			return;
		}

		mBluetoothGatt.writeCharacteristic(characteristic);
	}

	/**
	 * Request a read on a given {@code BluetoothGattCharacteristic}. The read
	 * result is reported asynchronously through the
	 * {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
	 * callback.
	 *
	 * @param characteristic
	 *            The characteristic to read from.
	 */
	public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
		if (mBluetoothAdapter == null || mBluetoothGatt == null) {
			Logutil.w("BluetoothAdapter not initialized");
			return;
		}
		mBluetoothGatt.readCharacteristic(characteristic);
	}

	/**
	 * Enables or disables notification on a give characteristic.
	 *
	 * @param characteristic
	 *            Characteristic to act on.
	 * @param enabled
	 *            If true, enable notification. False otherwise.
	 */
	public void setCharacteristicNotification(
			BluetoothGattCharacteristic characteristic, boolean enabled) {
		if (mBluetoothAdapter == null || mBluetoothGatt == null) {
			Logutil.w("BluetoothAdapter not initialized");
			return;
		}
		mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
		BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID
				.fromString(Constant.CLIENT_CHARACTERISTIC_CONFIG));
		if (descriptor != null) {
			Logutil.d("-----------write descriptor---------");
			descriptor
			.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
			mBluetoothGatt.writeDescriptor(descriptor);
		}
	}

	/**
	 * Retrieves a list of supported GATT services on the connected device. This
	 * should be invoked only after {@code BluetoothGatt#discoverServices()}
	 * completes successfully.
	 *
	 * @return A {@code List} of supported services.
	 */
	public List<BluetoothGattService> getSupportedGattServices() {
		if (mBluetoothGatt == null)
			return null;
		return mBluetoothGatt.getServices();
	}

	public void setInfo(int level,int usersex,int age,float high){
		this.level=level;
		this.usersex=usersex;
		this.age=age;
		this.high=high;
	}

	/**
	 * Write data to the hardware
	 * @param sum
	 */
	public void sendLight(byte[] sum) {
		if (characteristicWrite!= null&& mBluetoothGatt != null) {
			Logutil.e("-----设置写成功-------");
			characteristicWrite.setValue(sum);
			Logutil.e("####"+ mBluetoothGatt
					.writeCharacteristic(characteristicWrite)
					+ "       " + Utils.bytesToHexString(sum));
		}
	}

	/**
	 * Read the RSSI for a connected remote device.
	 */
	public boolean getRssiVal() {
		if (mBluetoothGatt == null)
			return false;
		return mBluetoothGatt.readRemoteRssi();
	}
}
